home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / umich / telecomm / sticpsrc.lzh / SOURCE.ARC / ATARI.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-10-06  |  61.9 KB  |  2,377 lines

  1. /*
  2.  * Atari ST-specific functions
  3.  * written by Rob Janssen, PE1CHL
  4.  * 3 configurations are possible:
  5.  *    .TOS
  6.  *    .ACC    define GEM and GEMACC
  7.  *    .PRG    define GEM and GEMPRG
  8.  */
  9.  
  10. #include <stdio.h>
  11. #include <ctype.h>
  12. #ifdef MWC
  13. # include <osbind.h>            /* os interface defines */
  14. # define Kbshift    Getshift    /* MW goofed... */
  15. #endif
  16. #ifdef __TURBOC__
  17. # define cdecl
  18. # include <tos.h>            /* os interface defines */
  19. #endif
  20. #include <linea.h>            /* line-A interface defines */
  21. #include <time.h>
  22.  
  23. #ifdef __TURBOC__            /* line-A access for Turbo C */
  24. # define VPLANES    Linea->v_planes
  25. # define V_CEL_HT    Vdiesc->v_cel_ht
  26. # define V_CEL_MX    Vdiesc->v_cel_mx
  27. # define V_CEL_MY    Vdiesc->v_cel_my
  28. # define V_CUR_AD    ((char *) (Vdiesc->v_cur_ad))
  29. # define V_CUR_OFF    Vdiesc->v_cur_off
  30. # define V_CUR_CX    Vdiesc->v_cur_xy[0]
  31. # define V_CUR_CY    Vdiesc->v_cur_xy[1]
  32. # define V_FNT_AD    ((char *) (Vdiesc->v_fnt_ad))
  33. #endif
  34.  
  35. #include "global.h"
  36. #include "config.h"
  37. #include "mbuf.h"
  38. #include "internet.h"
  39. #include "iface.h"
  40. #include "atari.h"
  41. #include "timer.h"
  42. #include "environ.h"
  43.  
  44. extern unsigned nasy;
  45.  
  46. /* Interface list header */
  47. struct interface *ifaces;
  48.  
  49. #ifdef OUTBUF
  50. static char *ttbuf;
  51. #endif
  52.  
  53. int  auxcon = 0;            /* auxcon control word */
  54. #define AUXCOUT        0x01        /* send out to AUX */
  55. #define AUXCNOT        0x02        /* don't send out to screen */
  56. #define AUXCIN        0x10        /* read input from AUX */
  57. int  auxitrn[256];            /* aux input translate table */
  58. char auxotrn[256];            /* aux output translate table */
  59.  
  60. int  screenmode = 0;            /* screen output control word */
  61. #define SCRTOS        0        /* output via TOS */
  62. #define SCRBIOS        1        /* output via BIOS */
  63. #define SCRDIR        2        /* direct output to screen */
  64.  
  65. extern void trap_x();            /* handler for trap #5/#11 */
  66. void (*trap_x_save)();            /* saved trap #5/#11 handler */
  67.  
  68. extern char startup[];            /* startup file name */
  69.  
  70. extern FILE *logfp;            /* log file pointer */
  71. extern char logname[];            /* log file name */
  72.  
  73. extern FILE *trfp;            /* trace file pointer */
  74. extern char trname[];            /* trace file name */
  75.  
  76. #ifdef GEM
  77.  
  78. #define     WORD        int        /* signed word (16 bits) */
  79. #define     TRUE        (1)        /* TRUE     value */
  80. #define     FALSE        (0)        /* FALSE value */
  81.  
  82. #include <gemdefs.h>            /* MW GEM definitions */
  83. #include <obdefs.h>
  84.  
  85. #define     MAXWIN        8        /* maximal window handle from GEM */
  86. #define     USEWIN        2        /* windows used by this application */
  87.  
  88. #define     DESK        0        /* window number for desktop */
  89. #define     ALIGN(x,n)    (x) = (n * (((x) + (n / 2)) / n))/* align x to n incr. */
  90.  
  91. #define     WI_KIND    (HSLIDE|RTARROW|LFARROW|VSLIDE|DNARROW|UPARROW|SIZER|MOVER|FULLER|CLOSER|NAME)
  92.  
  93. #define     MIN_WIDTH    (16 * gr_hwbox) /* minimal window width */
  94. #define     MIN_HEIGHT    (5 * gr_hhbox)    /* minimal window height */
  95.  
  96. #define     MAXLINE    100        /* history lines saved */
  97. #define     MAXCHAR    80        /* max characters on each line */
  98.  
  99. /*** datatypes ***/
  100.  
  101. typedef struct linehist            /* define 'line history' type */
  102. {
  103.    struct linehist *next;        /* forward link */
  104.    struct linehist *prev;        /* backward link */
  105.    int    len;                /* this one's length */
  106.    char line[1];            /* the actual line to be stored */
  107. } LINEHIST;
  108.  
  109. typedef struct                /* define 'sliding window' type */
  110. {
  111.    long total;                /* total size in this dimension */
  112.    long pos;                /* pos of portion currently seen */
  113.    int    seen;                /* size of portion currently seen */
  114. } SLWIN;
  115.  
  116. typedef struct window            /* info about windows */
  117. {
  118.    WORD     win;                /* corresponding window handle */
  119.    WORD     kind;                /* this window's wi_kind */
  120.    WORD     pobj;                /* object selected there */
  121.    GRECT work;                /* work area pos/size */
  122.    GRECT full;                /* full size of window */
  123.    GRECT org;                /* originating pos/size for shrink */
  124.    SLWIN hori;                /* horizontal slider info */
  125.    SLWIN vert;                /* vertical slider info */
  126.    LINEHIST *head;            /* head of line history */
  127.    LINEHIST *tail;            /* tail of line history */
  128.    int     curpos;            /* position on current line */
  129.    char     curline[MAXCHAR + 1];        /* current line */
  130.    char     cursor;            /* cursor state */
  131.    char     open;                /* window opened? */
  132.    char     title[MAX_LEN];        /* window title (if any) */
  133.    char     info[MAX_LEN];            /* window info (if any) */
  134. } WINDOW;
  135.  
  136. /*** GLOBAL VARIABLES ***/
  137.  
  138. /* GEM handles, information and the like */
  139.  
  140. extern WORD gl_apid;            /* application id (external to MWC) */
  141. WORD gl_mnid;                /* menu id for this accessory */
  142. WORD gr_handle;                /* physical workstation handle */
  143. static WORD handle;            /* virtual workstation handle */
  144.  
  145. WORD gr_hhchar,gr_hwchar,gr_hhbox,gr_hwbox;/* system font sizes */
  146.  
  147. /* window management datastructures */
  148.  
  149. static WINDOW window[USEWIN];        /* window info, DESK + user windows */
  150. static WORD win[MAXWIN];        /* handle to application conv. */
  151. static WORD topwin = -1;        /* top (=current) window handle */
  152.  
  153. static WORD await = MU_MESAG|MU_TIMER;    /* select initially active events */
  154. static GRECT m1,m2;            /* mouse event rectangles */
  155. static WORD m1flag,m2flag;        /* flags for mouse events 1,2 */
  156.  
  157. static WORD keyqueue = 0;        /* last typed character */
  158. static WORD keyqstat = 0;        /* it's shift status */
  159. static WORD fastpoll = 50;        /* fast poll interval */
  160.  
  161. WORD work_in[] = {1,1,1,1,1,1,1,1,1,1,2};/* Input to GSX parameter array */
  162. WORD work_out[57];            /* Output from GSX parameter array */
  163.  
  164. WORD contrl[12];            /* storage wasted for bindings */
  165. WORD intin[128];
  166. WORD ptsin[128];
  167. WORD intout[128];
  168. WORD ptsout[128];
  169.  
  170. /* the resource file symbols */
  171.  
  172. #ifdef GEMPRG
  173. # include "atari_rs.h"
  174. extern OBJECT *rs_trindex[];
  175. #endif
  176.  
  177. /* forward declarations (when necessary) */
  178.  
  179. void pop_up();
  180. void wi_getwork();
  181. void wi_close();
  182. void sl_update();
  183. void rq_redraw();
  184. WORD do_form();
  185. WORD *rc2pts();
  186. int  wputc();
  187.  
  188. #else                    /* not GEM */
  189.  
  190. # define CONTERM *((char *) 0x484L)    /* system's conterm byte */
  191. char    sconterm;            /* saved conterm byte */
  192.  
  193. char *logbase;                /* logical screen base */
  194.  
  195. int    cursor = 1;            /* cursor currently visible? */
  196. # ifdef MWC
  197. extern    int _fputt();            /* standard output putc routine */
  198. # endif
  199. #endif
  200.  
  201. #ifndef GEMACC
  202. # ifdef MWC
  203. long *_stksize = (long *) 4096;        /* stack size in bytes, becomes ptr */
  204. #  define STACK _stksize
  205. # endif
  206. # ifdef __TURBOC__
  207. extern long *_StkLim;            /* initialized to stack limit */
  208. #  define STACK _StkLim
  209. # endif
  210.  
  211. # define STKMAGIC 0x31415927L        /* magic to check overflow */
  212. #endif
  213.  
  214. void eihalt();
  215.  
  216. #ifndef GEMACC
  217. #ifdef __TURBOC__
  218. static void watchtick(void,...);    /* function called at timer tick */
  219. static void (*orgtick)(void,...);    /* original timertick handler */
  220. #else
  221. static void watchtick();        /* function called at timer tick */
  222. static void (*orgtick)();        /* original timertick handler */
  223. #endif
  224. static long watchcnt;            /* actual watchdog counter */
  225. static long watchnet;            /* watchdog while in NET */
  226. static long watchsh;            /* watchdog while in SHELL */
  227. static unsigned watchhw;        /* watchdog hardware reset */
  228. #endif
  229.  
  230. static void lat_start(),lat_stop(),lat_raw(),lat_status();
  231.  
  232.  
  233. /* Called at startup time to set up console I/O, memory heap */
  234. /* also sets and saves lots of other things on the Atari.... */
  235.  
  236. ioinit()
  237. {
  238. #ifdef GEM
  239.     WORD d;
  240. # ifdef GEMACC
  241.     char net[30];
  242. # endif
  243. #else
  244.     long sav_ssp;            /* saved stackpointer for Super() */
  245. #endif
  246.  
  247. #if (defined(MWC) && (MWC < 300))    /* pre-3.0 MW C? */
  248.     /* set the handler for trap #5. this will be called using the */
  249.     /* micro_rtx() function, and will disable/enable interrupts */
  250.     trap_x_save = (void (*)()) Setexc(0x25,trap_x);
  251. #else
  252.     /* set the handler for trap #7. this will be called using the */
  253.     /* _VtrapB_() function, and will disable/enable interrupts */
  254.     trap_x_save = (void (*)()) Setexc(0x27,trap_x);
  255. #endif
  256.  
  257. #ifndef GEMACC
  258.     STACK[0] = STACK[16] =        /* mark stack for overflow check */
  259.     STACK[32] = STKMAGIC;
  260.  
  261.     orgtick = (void (*)()) Setexc(0x100,watchtick); /* install watchdog ticker */
  262. #endif
  263.  
  264. #if (OUTBUF && !GEM)
  265.     ttbuf = malloc(BUFSIZ);
  266.     setbuf(stdout,ttbuf);
  267. #endif
  268.  
  269. #ifdef GEM
  270.     /* re-route output from "stdout" to window handler */
  271.  
  272.     stdout->_pt = wputc;        /* character output handler */
  273.     stdout->_ff |= _FERR;        /* to disable fflush() */
  274.  
  275.     if (!appl_init() || gl_apid < 0) /* init application */
  276.         exit(1);        /* exit if error (fatal) */
  277.  
  278.     gr_handle = graf_handle(&gr_hwchar,&gr_hhchar,&gr_hwbox,&gr_hhbox);
  279.  
  280.     handle = gr_handle;        /* open virtual workstation */
  281.     v_opnvwk(work_in,&handle,work_out); /* handle:=virtual ws handle */
  282.  
  283.     vst_alignment(handle,0,5,&d,&d); /* set text alignment */
  284.  
  285. # ifdef GEMPRG
  286.     rsrc_fixup();            /* fix the in-memory resource */
  287.     menu_bar(rs_trindex[MENUBAR],TRUE); /* show the menu bar */
  288. # endif
  289.  
  290. # ifdef GEMACC
  291.     if ((gl_mnid = menu_register(gl_apid, "  KA9Q TCP/IP Net ")) < 0)
  292.         exit(2);
  293. # endif
  294.  
  295.     window[0].win = DESK;        /* init desktop window descr. */
  296.     window[0].open = TRUE;
  297.     wi_getwork(0);            /* get workarea of desktop */
  298.     win[DESK] = 0;            /* remember it's handle */
  299.  
  300. # ifdef GEMPRG
  301.     rs_trindex[BACKDROP][ROOT].ob_x = window[DESK].work.g_x;/* set size to full desktop */
  302.     rs_trindex[BACKDROP][ROOT].ob_y = window[DESK].work.g_y;
  303.     rs_trindex[BACKDROP][ROOT].ob_width = window[DESK].work.g_w;
  304.     rs_trindex[BACKDROP][ROOT].ob_height = window[DESK].work.g_h;
  305.  
  306.     ob_draw(DESK,rs_trindex[BACKDROP],ROOT); /* draw the backdrop */
  307.  
  308.     wind_set(DESK,WF_NEWDESK,rs_trindex[BACKDROP],ROOT);
  309. # endif
  310.  
  311.     window[1].hori.total = MAXCHAR; /* init window 1 description */
  312.     window[1].vert.total = 1;    /* always one current line... */
  313.  
  314.     m1.g_x = m1.g_y = m1.g_w = m1.g_h =  /* no mouse event #1 */
  315.     m2.g_x = m2.g_y = m2.g_w = m2.g_h =  /* no mouse event #2 */
  316.     m1flag = m2flag = 0;        /* dummy flag (entry) */
  317.  
  318. # ifdef GEMACC
  319.     for (d = 0; d < 50; d++)    /* allow others to initialize */
  320.         eihalt();
  321. # endif
  322.  
  323. # ifdef GEMPRG
  324.     pop_up();
  325.     graf_mouse(ARROW);        /* default mouse form */
  326. # endif
  327.  
  328. # ifdef GEMACC
  329.     if ((d = Dgetdrv()) >= 2)    /* a harddisk system? (??) */
  330.     {
  331.        strcpy(net,startup);        /* find startup file */
  332.  
  333.        do
  334.        {
  335.           net[0] = d + 'a';        /* try C-P */
  336.        } while (access(net,4) && ((++d < 16) || (d = 0)));
  337.  
  338.        Dsetdrv(d);            /* move to where it's found */
  339.     }
  340. # endif
  341. #else                    /* not GEM */
  342.     sav_ssp = Super(NULL);        /* switch to supervisor mode */
  343.     sconterm = CONTERM;        /* read present console flags */
  344.     CONTERM |= 0x08;        /* set the "return shift state" flag */
  345.     Super(sav_ssp);            /* switch-back to user mode */
  346. #endif
  347.  
  348.     atari_init('n');        /* general initialization */
  349. }
  350.  
  351. /* Called just before exiting to restore console state (and others)
  352.  */
  353.  
  354. iostop()
  355. {
  356.     long sav_ssp;            /* saved stackpointer for Super() */
  357. #ifdef GEMPRG
  358.     WORD wi;
  359.  
  360.     graf_mouse(HOURGLASS);        /* revert to 'wait' mouseform */
  361.  
  362.     for (wi = 1; wi < MAXWIN; wi++) /* close the windows */
  363.         if (win[wi] != 0)        /* when they are in use */
  364.         wi_close(wi);
  365.  
  366.     menu_bar(rs_trindex[MENUBAR],FALSE); /* remove the menubar */
  367.     v_clsvwk(handle);        /* close virtual workstation */
  368.     appl_exit();            /* exit from application */
  369. #endif
  370.  
  371. #if (OUTBUF && !GEM)
  372.     setbuf(stdout,NULLCHAR);
  373.     free(ttbuf);
  374. #endif
  375.     while(ifaces != NULLIF){
  376.         /* first flush the interface's buffers */
  377.         while(ifaces->recv != NULLVFP && (*ifaces->recv)(ifaces))
  378.             ;
  379.         if(ifaces->stop != NULLFP)
  380.             (*ifaces->stop)(ifaces);
  381.         ifaces = ifaces->next;
  382.     }
  383.  
  384. #ifndef GEM
  385.     while (kbread() != -1)        /* eat remaining chars, cursor ON */
  386.         ;
  387.  
  388.     sav_ssp = Super(NULL);        /* switch to supervisor mode */
  389.     CONTERM = sconterm;        /* restore value saved at startup */
  390.     Super(sav_ssp);            /* switch-back to user mode */
  391. #endif
  392.  
  393.     atari_stop();            /* general termination */
  394.  
  395.     lat_stop();            /* stop latency measurement */
  396.  
  397. #ifndef GEMACC
  398.     Setexc(0x100,orgtick);        /* restore original timer tick */
  399. #endif
  400. #if (defined(MWC) && (MWC < 300))    /* pre-3.0 MW C? */
  401.     Setexc(0x25,trap_x_save);    /* remove our trap #5 handler */
  402. #else
  403.     Setexc(0x27,trap_x_save);    /* remove our trap #7 handler */
  404. #endif
  405.  
  406.     sav_ssp = Super(NULL);        /* switch to supervisor mode */
  407.     *((long *) 0x3fc) = 0x0L;    /* erase signature (see log proc) */
  408.     Super(sav_ssp);            /* switch-back to user mode */
  409. }
  410.  
  411. /* checks the time then ticks and updates ISS */
  412. void
  413. check_time()
  414. {
  415.     int32 iss();
  416.     int maxticks = (int) SEC2TICK(15); /* upper limit on delayed ticks */
  417.     static unsigned long clkval = 0;
  418.  
  419. #ifndef NOCLOCK
  420.     clock_t clknow = clock() / 20L; /* ANSI routine to read clock */
  421. #else
  422.     long sav_ssp;            /* saved stackpointer for Super() */
  423.     unsigned long clknow;        /* current clock value */
  424.  
  425.     sav_ssp = Super(NULL);        /* switch to supervisor mode */
  426.     clknow = *((unsigned long *) 0x4baL) / 20L; /* read 200Hz timer value, make 10/sec */
  427.     Super(sav_ssp);            /* switch-back to user mode */
  428. #endif
  429.  
  430.     if (clkval == 0)        /* first time? */
  431.         clkval = clknow;
  432.  
  433.     while(clkval < clknow){
  434. #ifdef IP
  435.         icmpclk();        /* Call this one before tick */
  436. #endif
  437.         tick();
  438. #ifdef IP
  439.         (void)iss();
  440. #endif
  441.         if (maxticks--)        /* check a limit on ticks */
  442.             clkval++;
  443.         else
  444.             clkval = clknow; /* thereafter, ignore them */
  445.     }
  446. }
  447.  
  448. #ifdef MWC
  449. /* Mark Williams C reads the keyboard clock on the first call to time() */
  450. /* using the Kgettime() routine. Unfortunately, this clock has moved to */
  451. /* a different place in the Mega ST, so we'll redefine Kgettime to call */
  452. /* the xbios routine that will read both the clocks (MW should do that!)*/
  453.  
  454. tm_t *Kgettime ()
  455.  
  456. {
  457.    static tm_t rv;            /* returned value is static struct */
  458.    register tetd_t tim;            /* value from xbios */
  459.  
  460.    tim = Gettime();            /* reads keyboard or RTC chip */
  461.  
  462.    rv.tm_sec = (tim & 0x1f) * 2;    /* silly 2-second time increment */
  463.    rv.tm_min = (tim >> 5) & 0x3f;
  464.    rv.tm_hour = (tim >> 11) & 0x1f;
  465.    rv.tm_mday = (tim >> 16) & 0x1f;
  466.    rv.tm_mon = ((tim >> 21) & 0x0f) - 1;
  467.    rv.tm_year = ((tim >> 25) & 0x7f) + 80;
  468.    /* note: tm_wday, tm_yday and tm_isdst not set */
  469.  
  470.    return (&rv);
  471. }
  472. #endif
  473.  
  474. /* Read characters from the keyboard, translating them to "real" ASCII
  475.  * If none are ready, return -1
  476.  */
  477. int
  478. kbread()
  479. {
  480.     unsigned char key,scan,shift;
  481.     int  c;
  482.     static int altkeys = 0;        /* for alt-digit pressure */
  483.     static int altchar = 0;
  484. #ifdef GEM
  485.     int  x,y;
  486.     register WINDOW *curwin;
  487.     char outstr[4];
  488. #else
  489.     long keychar;
  490. #endif
  491.  
  492.     if (auxcon & AUXCIN)        /* check the aux port for chars? */
  493.     {
  494.        if (Bconstat(1))        /* a character ready? */
  495.           return (auxitrn[uchar(Bconin(1))]); /* read it and translate */
  496.     }
  497.  
  498. #ifdef GEM
  499.     if (!keyqueue)            /* no char typed? */
  500.     {
  501.         if (topwin > 0 && win[topwin])    /* our window on top? */
  502.         {
  503.             curwin = &window[win[topwin]];
  504.  
  505.             if (curwin->open && !curwin->cursor &&
  506.                 curwin->curpos >= curwin->hori.pos &&
  507.                 (y = curwin->vert.total -
  508.                  curwin->vert.pos - 1) <= curwin->vert.seen)
  509.             {
  510.                 vs_clip(handle,TRUE,rc2pts(&curwin->work));
  511.  
  512.                 x = curwin->work.g_x + 1 +
  513.                     (curwin->curpos - curwin->hori.pos) * gr_hwchar;
  514.  
  515.                 y = curwin->work.g_y + y * gr_hhchar;
  516.  
  517.                 outstr[0] = 240;    /* cursor char */
  518.                 outstr[1] = ' ';
  519.                 outstr[2] = '\0';
  520.                 v_gtext(handle,x,y,outstr);
  521.  
  522.                 curwin->cursor = 1;
  523.             }
  524.         }
  525.  
  526.         return (-1);
  527.     }
  528.  
  529.     key = keyqueue;            /* get ascii part */
  530.     scan = keyqueue >> 8;        /* get scancode */
  531.     shift = keyqstat & 0x1f;    /* get shift status */
  532.     keyqueue = keyqstat = 0;    /* keycode used, clear it */
  533. #else
  534.     if (!Cconis()){            /* any char available? */
  535.         if (!(Kbshift(-1) & 0x08)){ /* no, Alternate not pressed? */
  536.             if (altkeys){
  537.                 c = altchar;
  538.                 altchar = altkeys = 0;
  539.                 return (c);
  540.             }
  541.         }
  542.         if (!cursor){        /* cursor visible? */
  543.             cursor = 1;
  544.             Bconout(2,'\033'); /* no, switch it on */
  545.             Bconout(2,'e');
  546.         }
  547.         return (-1);        /* return -1 for 'no chars' */
  548.     }
  549.  
  550.     keychar = Crawcin();        /* read char, no echo */
  551.     key = keychar;            /* get ascii part */
  552.     scan = keychar >> 16;        /* get scancode */
  553.     shift = (keychar >> 24) & 0x1f; /* get shift status */
  554. #endif
  555.  
  556.     if ((shift & 0x08) &&        /* Alternate key pressed? */
  557.         scan >= 103 && scan <= 112 && /* numeric keypad entry */
  558.         key >= '0' && key <= '9')    /* more verification of numeric */
  559.     {
  560.         altchar = 10 * altchar + key - '0'; /* compute code */
  561.  
  562.         if (++altkeys == 3){    /* code complete? */
  563.             c = altchar;
  564.             altchar = altkeys = 0;
  565.             return (c);
  566.         } else {
  567.             return (-1);    /* incomplete, return no char */
  568.         }
  569.     }
  570.  
  571.     altchar = altkeys = 0;        /* no Alt - wipe Alternate char */
  572.  
  573.     if((c = key) == 0){
  574.         /* a special char */
  575.         switch(scan)        /* examine scancode */
  576.         {
  577.         case 3:            /* NULL (bizzare!) */
  578.             c = 0;
  579.             break;
  580.         case 59:        /* F-1 key */
  581.             c = -11;
  582.             break;
  583.         case 60:        /* F-2 key */
  584.             c = -10;
  585.             break;
  586.         case 61:        /* F-3 key */
  587.             c = -9;
  588.             break;
  589.         case 62:        /* F-4 key */
  590.             c = -8;
  591.             break;
  592.         case 63:        /* F-5 key */
  593.             c = -7;
  594.             break;
  595.         case 64:        /* F-6 key */
  596.             c = -6;
  597.             break;
  598.         case 65:        /* F-7 key */
  599.             c = -5;
  600.             break;
  601.         case 66:        /* F-8 key */
  602.             c = -4;
  603.             break;
  604.         case 67:        /* F-9 key */
  605.             c = -3;
  606.             break;
  607.         case 68:        /* F-10 key (used as command-mode escape) */
  608.             c = -2;
  609.             break;
  610.         case 71:        /* HOME key */
  611.             c = -20;
  612.             break;
  613.         case 72:        /* UP arrow */
  614.             c = -19;
  615.             break;
  616.         case 73:        /* PAGE UP */
  617.         case 98:        /* HELP key (used as PAGE UP) */
  618.             c = -18;
  619.             break;
  620.         case 75:        /* LEFT arrow */
  621.             c = -17;
  622.             break;
  623.         case 77:        /* RIGHT arrow */
  624.             c = -16;
  625.             break;
  626.         case 119:        /* END (ctrl-Home) key */
  627.             c = -15;
  628.             break;
  629.         case 80:        /* DOWN arrow */
  630.             c = -14;
  631.             break;
  632.         case 81:        /* PAGE DOWN */
  633.         case 97:        /* UNDO key (used as PAGE DOWN) */
  634.             c = -13;
  635.             break;
  636.         case 82:        /* INS key */
  637.             c = -12;
  638.             break;
  639.         case 83:        /* DEL key */
  640.             c = 0x7f;
  641.             break;
  642.         default:        /* Dunno what it is */
  643.             c = -1;
  644.         }
  645.     }
  646.  
  647.     return (c);
  648. }
  649.  
  650. #ifdef GEM
  651. /* utility routines only needed for GEM version */
  652.  
  653. /*** elementary window management routines ***/
  654.  
  655. /* align window in such a way that first character inside it starts */
  656. /* on a byte boundary.    a somewhat specialized function... */
  657.  
  658. void wi_align (rc)
  659.    register GRECT *rc;            /* window outside dimensions */
  660.  
  661. {
  662.    /* first, calculate work area */
  663.  
  664.    wind_calc(1,WI_KIND,rc->g_x,rc->g_y,rc->g_w,rc->g_h,
  665.               &rc->g_x,&rc->g_y,&rc->g_w,&rc->g_h);
  666.  
  667.    ALIGN(rc->g_x,8);            /* align on 8-bit boundary */
  668.  
  669.    if (--(rc->g_x) < 0)            /* one bit extra, left of char */
  670.       rc->g_x += 8;            /* if that makes it negative, bump */
  671.  
  672.    ALIGN(rc->g_w,gr_hwchar);        /* integer number of chars wide */
  673.    rc->g_w++;                /* one extra (left of first char) */
  674.  
  675.    ALIGN(rc->g_h,gr_hhchar);        /* integer number of lines high */
  676.  
  677.    /* re-calculate outside dimensions */
  678.  
  679.    wind_calc(0,WI_KIND,rc->g_x,rc->g_y,rc->g_w,rc->g_h,
  680.               &rc->g_x,&rc->g_y,&rc->g_w,&rc->g_h);
  681. }
  682.  
  683. /* create and open a window, and update window datatructure */
  684. /* return TRUE when the window could be created and opened  */
  685. /* assumes some fields of window[wi_num] already set up:    */
  686. /*   title,info,org                        */
  687.  
  688. int  wi_open (wi_num,kind,wsize)
  689.    WORD wi_num;                /* window NUMBER */
  690.    WORD kind;                /* window kind */
  691.    register GRECT *wsize;        /* desired pos/size rc */
  692.  
  693. {
  694.    register WINDOW *curwin = &window[wi_num];
  695.    GRECT rc;
  696.    WORD pts[4];                /* dummy for clip_off */
  697.  
  698.    if (!curwin->open)            /* not yet open? */
  699.    {
  700.       curwin->kind = kind;        /* remember kind of window */
  701.  
  702.       graf_growbox(curwin->org.g_x,curwin->org.g_y,
  703.            curwin->org.g_w,curwin->org.g_h,
  704.            wsize->g_x,wsize->g_y,wsize->g_w,wsize->g_h);
  705.  
  706.       rc_copy(&window[0].work,&curwin->full);/* max size is desksize */
  707.       wi_align(&curwin->full);        /* but, aligned to char bound */
  708.  
  709.       curwin->full.g_y = window[0].work.g_y +
  710.             (window[0].work.g_h - curwin->full.g_h) / 2;
  711.  
  712.       while (curwin->full.g_x + curwin->full.g_w >
  713.          window[0].work.g_x + window[0].work.g_w)
  714.      curwin->full.g_w -= gr_hwchar; /* make sure it will fit on desktop */
  715.  
  716.       if ((curwin->win = wind_create(curwin->kind,curwin->full.g_x,
  717.                           curwin->full.g_y,
  718.                           curwin->full.g_w,
  719.                           curwin->full.g_h)) < 0)
  720.       {
  721.      form_alert(1,"[3][|No more windows available][ SORRY ]");
  722.      return (FALSE);
  723.       }
  724.  
  725.       if (curwin->win >= MAXWIN)    /* unexpected window number? */
  726.       {
  727.      form_alert(1,"[3][|Unexpected window handle][ SORRY ]");
  728.      wind_delete(curwin->win);    /* destroy bad window */
  729.      return (FALSE);
  730.       }
  731.  
  732.       win[curwin->win] = wi_num;    /* remember number for handle */
  733.  
  734.       wind_set(curwin->win,WF_NAME,curwin->title);/* effectuate title */
  735.       wind_set(curwin->win,WF_INFO,curwin->info);/* effectuate info */
  736.  
  737.       wind_set(curwin->win,WF_HSLSIZE,1000);/* initially, full sliders */
  738.       wind_set(curwin->win,WF_VSLSIZE,1000);
  739.  
  740.       wind_update(BEG_UPDATE);        /* lock rectangle list */
  741.  
  742.       wind_open(curwin->win,wsize->g_x,wsize->g_y,/* actually open the window */
  743.                 wsize->g_w,wsize->g_h);
  744.  
  745.       curwin->open = TRUE;        /* remember it is open */
  746.       topwin = curwin->win;        /* it always comes on top */
  747.  
  748.       wi_getwork(curwin->win);        /* get size/pos info */
  749.       sl_update(curwin->win);        /* set sliders */
  750.  
  751.       rc_copy(&curwin->work,&rc);    /* get window workarea */
  752.       if (rc_intersect(&window[0].work,&rc))/* intersect with desktop */
  753.       {
  754.      vs_clip(handle,FALSE,pts);    /* clip off for speed */
  755.      vsf_interior(handle,0);    /* blank the work area */
  756.      vr_recfl(handle,rc2pts(&rc));
  757.       }
  758.  
  759.       wind_update(END_UPDATE);        /* unlock rectangle list */
  760.    }
  761.  
  762.    return (TRUE);            /* results okay! */
  763. }
  764.  
  765. /* close a window and update window datatructure */
  766.  
  767. void wi_close (wi)
  768.    WORD wi;                /* window handle */
  769.  
  770. {
  771.    register WINDOW *curwin = &window[win[wi]];
  772.    WORD d;                /* dummy for wind_get */
  773.  
  774.    if (curwin->open)            /* now open? */
  775.    {
  776.       wind_close(wi);            /* close the window */
  777.  
  778.       graf_shrinkbox(curwin->org.g_x,curwin->org.g_y,
  779.              curwin->org.g_w,curwin->org.g_h,
  780.              curwin->work.g_x,curwin->work.g_y,
  781.              curwin->work.g_w,curwin->work.g_h);
  782.  
  783.       if (wi == topwin)            /* was it the top window? */
  784.      wind_get(0,WF_TOP,&topwin,&d,&d,&d);/* get new top window */
  785.  
  786.       wind_delete(wi);            /* delete window */
  787.  
  788.       curwin->open = FALSE;        /* remember it is closed */
  789.       win[wi] = 0;            /* also forget the handle */
  790.    }
  791. }
  792.  
  793. /* get a window's current size and position */
  794. /* also sets amount seen for sliding scrollbars */
  795.  
  796. void wi_getwork (wi)
  797.    WORD wi;
  798.  
  799. {
  800.    register WINDOW *curwin = &window[win[wi]];
  801.  
  802.    wind_get(wi,WF_WORKXYWH,        /* get size and position info */
  803.         &curwin->work.g_x,&curwin->work.g_y,
  804.         &curwin->work.g_w,&curwin->work.g_h);
  805.  
  806.    /* set the 'amount seen' for each window */
  807.  
  808.    switch (win[wi])            /* determine application usage */
  809.    {
  810.    case 1:                /* output window */
  811.       curwin->hori.seen = curwin->work.g_w / gr_hwchar;
  812.       curwin->vert.seen = curwin->work.g_h / gr_hhchar;
  813.       break;
  814.    }
  815. }
  816.  
  817. /* window character-handling routines */
  818.  
  819. /* linefeed adds another line to the history, and updates window (scroll) */
  820.  
  821. static void linefeed (curwin)
  822.     register WINDOW *curwin;
  823.  
  824. {
  825.    register LINEHIST *new;
  826.  
  827.    if (curwin->vert.total > MAXLINE)    /* above the maximum? */
  828.    {
  829.       if (curwin->head != NULL)        /* should be! */
  830.       {
  831.      free(curwin->head);        /* release it */
  832.      curwin->head = curwin->head->next;
  833.      curwin->head->prev = NULL;
  834.      curwin->vert.total--;
  835.       }
  836.    }
  837.  
  838.    curwin->curline[curwin->curpos] = '\0';
  839.  
  840.    if ((new = malloc(sizeof(LINEHIST) + curwin->curpos)) == NULL)
  841.       return;                /* that's pity! */
  842.  
  843.    new->len = curwin->curpos;
  844.    strcpy(new->line,curwin->curline);
  845.  
  846.    if (curwin->tail == NULL)
  847.    {
  848.       curwin->head = curwin->tail = new;
  849.       new->prev = new->next = NULL;
  850.    }
  851.    else
  852.    {
  853.       new->prev = curwin->tail;
  854.       new->next = NULL;
  855.       curwin->tail->next = new;
  856.       curwin->tail = new;
  857.    }
  858.  
  859.    curwin->vert.total++;
  860.  
  861.    if (curwin->vert.pos >= curwin->vert.total - curwin->vert.seen - 1)
  862.       curwin->vert.pos = curwin->vert.total - curwin->vert.seen;
  863.  
  864.    curwin->curpos = 0;
  865.  
  866.    sl_update(curwin->win);        /* update sliders */
  867.  
  868.    if (curwin->vert.pos >= curwin->vert.total - curwin->vert.seen)
  869.    {
  870.       rq_redraw(curwin->win,&curwin->work); /* draw desired window */
  871.       eihalt();                /* handle messages */
  872.    }
  873. }
  874.  
  875. /* write a character to a window */
  876.  
  877. int wputc (c,outfile)
  878.    int c;
  879.    FILE *outfile;
  880.  
  881. {
  882.    int    wdw;
  883.    WORD x,y;
  884.    register WINDOW *curwin;
  885.    char outstr[2];
  886.  
  887.    if (outfile == stdout)        /* determine window# from handle */
  888.    {
  889.       if (auxcon & AUXCOUT)        /* standard output to AUX too? */
  890.       {
  891.      if (c == '\n')            /* if \n, add \r */
  892.         Bconout(1,auxotrn['\r']);
  893.  
  894.      Bconout(1,auxotrn[uchar(c)]);    /* send (translated) character */
  895.       }
  896.  
  897.       if (auxcon & AUXCNOT)        /* not to screen? */
  898.      return;            /* then we're done! */
  899.  
  900.       wdw = 1;                /* also to window 1 */
  901.    }
  902.    else
  903.       return (EOF);
  904.  
  905.    curwin = &window[wdw];        /* address output window */
  906.  
  907.    switch(c)                /* character interpreter */
  908.    {
  909.    case '\b': if (curwin->curpos)    /* backspace */
  910.            curwin->curpos--;
  911.  
  912.           break;
  913.  
  914.    case '\n': linefeed(curwin);        /* linefeed */
  915.           break;
  916.  
  917.    case '\r': break;            /* carriage return */
  918.  
  919.    case '\t': do            /* horizontal tab */
  920.           {
  921.          wputc(' ',outfile);
  922.           } while (curwin->curpos % 8);
  923.  
  924.           break;
  925.  
  926.    case '\a': Bconout(2,7);        /* bell */
  927.           if (!auxcon)
  928.          pop_up();        /* bring under close attention */
  929.           break;
  930.  
  931.    case '\0': break;            /* NUL */
  932.  
  933.    default:   curwin->curline[curwin->curpos++] = c; /* some other char */
  934.  
  935.           if (curwin->curpos >= MAXCHAR)    /* right margin? */
  936.          linefeed(curwin);    /* wrap to next line */
  937.           else
  938.           {
  939.          if (topwin > 0 && win[topwin] == wdw &&
  940.              curwin->curpos > curwin->hori.pos &&
  941.              (y = curwin->vert.total -
  942.               curwin->vert.pos - 1) <= curwin->vert.seen)
  943.          {
  944.             vs_clip(handle,TRUE,rc2pts(&curwin->work));
  945.  
  946.             x = curwin->work.g_x + 1 +
  947.             (curwin->curpos - 1 - curwin->hori.pos) * gr_hwchar;
  948.  
  949.             y = curwin->work.g_y + y * gr_hhchar;
  950.  
  951.             outstr[0] = c;
  952.             outstr[1] = '\0';
  953.             v_gtext(handle,x,y,outstr);
  954.          }
  955.          else
  956.             curwin->cursor = 2; /* must be updated */
  957.           }
  958.  
  959.           break;
  960.    }
  961.  
  962.    if (curwin->cursor != 2)
  963.       curwin->cursor = 0;        /* show cursor when needed */
  964.  
  965.    return (c);
  966. }
  967.  
  968. /* rq_redraw: request to redraw an area by sending myself a redraw message */
  969.  
  970. void rq_redraw (wi,area)
  971.    WORD     wi;                /* window handle */
  972.    register GRECT *area;        /* area to redraw */
  973.  
  974. {
  975.    WORD msg[8];                /* buffer for message */
  976.  
  977.    msg[0] = WM_REDRAW;            /* build a REDRAW message */
  978.    msg[1] = gl_apid;            /* coming from myself */
  979.    msg[2] = 0;                /* no more than std. 16 bytes */
  980.    msg[3] = wi;                /* redraw this window */
  981.    msg[4] = area->g_x;            /* area to redraw */
  982.    msg[5] = area->g_y;
  983.    msg[6] = area->g_w;
  984.    msg[7] = area->g_h;
  985.  
  986.    appl_write(gl_apid,sizeof(msg),msg); /* send myself the message */
  987. }
  988.  
  989. /*** the one-and-only window redraw routine ***/
  990.  
  991. /* do_redraw: find and redraw all clipping rectangles in an area */
  992. /* only to be called when a redraw message comes in (use rq_redraw) */
  993.  
  994. void do_redraw (wi,area)
  995.    WORD     wi;                /* window handle */
  996.    register GRECT *area;        /* area to redraw */
  997.  
  998. {
  999.    register WINDOW *curwin = &window[win[wi]];
  1000.    GRECT rc;                /* intersection area */
  1001.    WORD *pts;
  1002.    int    y,skip;
  1003.    LINEHIST *lin;
  1004.    char m_off = FALSE;            /* mouse already turned off? */
  1005.  
  1006.    if (!rc_intersect(&window[0].work,area))/* first intersect with desktop */
  1007.       return;                /* nothing to do! (unlikely) */
  1008.  
  1009.    wind_update(BEG_UPDATE);        /* lock rectangle list */
  1010.  
  1011.    /* get the first rectangle in the 'visible' list for this window */
  1012.  
  1013.    wind_get(wi,WF_FIRSTXYWH,&rc.g_x,&rc.g_y,&rc.g_w,&rc.g_h);
  1014.  
  1015.    while (rc.g_w && rc.g_h)        /* got a rectangle? */
  1016.    {
  1017.       if (rc_intersect(area,&rc))    /* is there an intersection? */
  1018.       {
  1019.      if (!m_off)            /* mouse already OFF? */
  1020.      {
  1021.         graf_mouse(M_OFF);        /* mouse OFF during updates */
  1022.         m_off = TRUE;        /* now it's off */
  1023.      }
  1024.  
  1025.      pts = rc2pts(&rc);        /* convert inters. area to points */
  1026.  
  1027.      vs_clip(handle,FALSE,pts);    /* clip off for speed */
  1028.      vsf_interior(handle,0);    /* blank the area */
  1029.      vr_recfl(handle,pts);
  1030.  
  1031.      vs_clip(handle,TRUE,pts);    /* clip on */
  1032.      y = curwin->work.g_y;        /* top drawing position */
  1033.  
  1034.      if ((lin = curwin->tail) != NULL) /* lines in history? */
  1035.      {
  1036.         skip = curwin->vert.total - curwin->vert.pos - 1; /* lines to skip back */
  1037.  
  1038.         while (--skip && lin->prev != NULL)
  1039.            lin = lin->prev;        /* work backwards */
  1040.      }
  1041.  
  1042.      while (lin != NULL)        /* quit when all printed */
  1043.      {
  1044.         if (lin->len > curwin->hori.pos)
  1045.            v_gtext(handle,curwin->work.g_x + 1,y,
  1046.                lin->line + curwin->hori.pos);
  1047.  
  1048.         if ((y += gr_hhchar) > rc.g_y + rc.g_h)
  1049.            break;
  1050.  
  1051.         lin = lin->next;
  1052.      }
  1053.  
  1054.      /* if still space, also show current (partial) line */
  1055.  
  1056.      if (y <= rc.g_y + rc.g_h && curwin->curpos > curwin->hori.pos)
  1057.      {
  1058.         curwin->curline[curwin->curpos] = '\0';
  1059.         v_gtext(handle,curwin->work.g_x + 1,y,
  1060.             curwin->curline + curwin->hori.pos);
  1061.      }
  1062.       }
  1063.  
  1064.       wind_get(wi,WF_NEXTXYWH,&rc.g_x,&rc.g_y,&rc.g_w,&rc.g_h);
  1065.    }
  1066.  
  1067.    if (m_off)                /* did we turn it OFF? */
  1068.       graf_mouse(M_ON);            /* mouse can now be ON again */
  1069.  
  1070.    wind_update(END_UPDATE);        /* unlock rectangle list */
  1071.    curwin->cursor = 0;            /* should re-display cursor now */
  1072. }
  1073.  
  1074. /*
  1075.  * set the sliders as required.
  1076.  *
  1077.  * the SLWIN structures 'hori' and 'vert' are used as input.
  1078.  * inconsistencies in the position are corrected.
  1079.  * the sliders are only set when required.
  1080.  *
  1081.  */
  1082.  
  1083. void sl_update (wi)
  1084.    WORD wi;                /* window handle */
  1085.  
  1086. {
  1087.    register WINDOW *curwin = &window[win[wi]];
  1088.    register WORD size,pos;        /* computed size & position */
  1089.    register long maxpos;        /* maximal position */
  1090.    WORD now,dummy;            /* current value */
  1091.  
  1092.    if (curwin->kind & HSLIDE)        /* horizontal slider in use? */
  1093.    {
  1094.       maxpos = curwin->hori.total - curwin->hori.seen;/* max allowed pos */
  1095.  
  1096.       if (curwin->hori.pos > maxpos)    /* bring position within range */
  1097.      curwin->hori.pos = maxpos;
  1098.  
  1099.       if (curwin->hori.pos < 0)
  1100.      curwin->hori.pos = 0;
  1101.  
  1102.       if (curwin->hori.seen < curwin->hori.total)/* partial view? */
  1103.       {
  1104.      pos = (1000L * curwin->hori.pos) / maxpos;
  1105.      size = (1000L * curwin->hori.seen) / curwin->hori.total;
  1106.       }
  1107.       else                /* we have full-view! */
  1108.       {
  1109.      pos = 0;            /* set slider to full size */
  1110.      size = 1000;
  1111.       }
  1112.  
  1113.       wind_get(wi,WF_HSLIDE,&now,&dummy,&dummy,&dummy);/* get pos */
  1114.  
  1115.       if (pos != now)            /* update needed? */
  1116.      wind_set(wi,WF_HSLIDE,pos);    /* then do it */
  1117.  
  1118.       wind_get(wi,WF_HSLSIZE,&now,&dummy,&dummy,&dummy);/* get size */
  1119.  
  1120.       if (size != now)            /* update needed? */
  1121.      wind_set(wi,WF_HSLSIZE,size);    /* then do it */
  1122.    }
  1123.  
  1124.    if (curwin->kind & VSLIDE)        /* vertical slider in use? */
  1125.    {
  1126.       maxpos = curwin->vert.total - curwin->vert.seen;/* max allowed pos */
  1127.  
  1128.       if (curwin->vert.pos > maxpos)    /* bring position within range */
  1129.      curwin->vert.pos = maxpos;
  1130.  
  1131.       if (curwin->vert.pos < 0)
  1132.      curwin->vert.pos = 0;
  1133.  
  1134.       if (curwin->vert.seen < curwin->vert.total)/* partial view? */
  1135.       {
  1136.      pos = (1000L * curwin->vert.pos) / maxpos;
  1137.      size = (1000L * curwin->vert.seen) / curwin->vert.total;
  1138.       }
  1139.       else                /* we have full-view! */
  1140.       {
  1141.      pos = 0;            /* set slider to full size */
  1142.      size = 1000;
  1143.       }
  1144.  
  1145.       wind_get(wi,WF_VSLIDE,&now,&dummy,&dummy,&dummy);/* get pos */
  1146.  
  1147.       if (pos != now)            /* update needed? */
  1148.      wind_set(wi,WF_VSLIDE,pos);    /* then do it */
  1149.  
  1150.       wind_get(wi,WF_VSLSIZE,&now,&dummy,&dummy,&dummy);/* get size */
  1151.  
  1152.       if (size != now)            /* update needed? */
  1153.      wind_set(wi,WF_VSLSIZE,size);    /* then do it */
  1154.    }
  1155. }
  1156.  
  1157. # ifdef GEMPRG
  1158. /* handle menu selections */
  1159.  
  1160. void mn_select (menu_entry)
  1161.    WORD menu_entry;            /* entry index */
  1162.  
  1163. {
  1164.    GRECT text_rc;            /* menu entry text x, y, w and h */
  1165.  
  1166.    switch (menu_entry)            /* the menu entry index */
  1167.    {
  1168.    case ENABOUT:            /* about this program... */
  1169.       ob_rect(rs_trindex[MENUBAR],ENABOUT,&text_rc);/* get menu entry info */
  1170.       do_form(rs_trindex[ABOUT],&text_rc);/* display 'ABOUT' dialogue */
  1171.       break;
  1172.  
  1173.    case ENOPEN:                /* open window */
  1174.       pop_up();
  1175.       break;
  1176.  
  1177.    case ENQUIT:                /* quit program */
  1178.       doexit(0);
  1179.       break;
  1180.    }
  1181. }
  1182.  
  1183. /*
  1184.  * display an input form, await respons,ttnd remove it
  1185.  * only the exitbox is deselected on return, others must be deselected
  1186.  * by the caller! (after testing their state)
  1187.  *
  1188.  * the tree address of the object (from rsrc_gaddr) is passed,
  1189.  * together with the originating position of the growing box.
  1190.  * a NULL org box will suppress the grow/shrink effects.
  1191.  * the index of the exit object is returned.
  1192.  *
  1193.  */
  1194.  
  1195. WORD do_form (taddr,org)
  1196.    register OBJECT *taddr;        /* form tree address */
  1197.    register GRECT  *org;        /* originating pos of growing box */
  1198.  
  1199. {
  1200.    register WORD exitobj;        /* button used to exit form */
  1201.    register char exitform = FALSE;    /* hit real exit button? */
  1202.    WORD fo_x,fo_y,fo_w,fo_h;        /* form position */
  1203.  
  1204.    form_center(taddr,&fo_x,&fo_y,&fo_w,&fo_h);/* compute position */
  1205.  
  1206.                     /* reserve screen space */
  1207.    form_dial(FMD_START,0,0,0,0,fo_x,fo_y,fo_w,fo_h);
  1208.  
  1209.    if (org != NULL)            /* growing boxes wanted? */
  1210.       form_dial(FMD_GROW,org->g_x,org->g_y,org->g_w,org->g_h,
  1211.              fo_x,fo_y,fo_w,fo_h);
  1212.  
  1213.    objc_draw(taddr,ROOT,MAX_DEPTH,fo_x,fo_y,fo_w,fo_h);/* draw the form */
  1214.  
  1215.    while (!exitform)            /* loop until real exit */
  1216.    {
  1217.       exitobj = form_do(taddr,0) & 0x7fff;/* interact with form */
  1218.  
  1219.       switch (taddr[exitobj].ob_type >> 8)/* get 'user type' of exitobj */
  1220.       {
  1221.       case 0:                /* no user type, real exit */
  1222.      exitform = TRUE;
  1223.      break;
  1224.  
  1225.       default:                /* some unknown thing */
  1226.      break;                /* just re-enter */
  1227.       }
  1228.    }
  1229.  
  1230.    if (org != NULL)            /* shrinking box wanted? */
  1231.       form_dial(FMD_SHRINK,org->g_x,org->g_y,org->g_w,org->g_h,
  1232.                fo_x,fo_y,fo_w,fo_h);
  1233.  
  1234.                     /* redraw screen */
  1235.    form_dial(FMD_FINISH,0,0,0,0,fo_x,fo_y,fo_w,fo_h);
  1236.  
  1237.    taddr[exitobj].ob_state &= ~SELECTED;/* reset exit to 'not selected' */
  1238.    return (exitobj);            /* return the exit object# */
  1239. }
  1240. # endif
  1241.  
  1242. /* convert GRECT to an array containing 2 vertices */
  1243. /* return a pointer to the result (static area overwritten by each call) */
  1244.  
  1245. WORD *rc2pts (grect)
  1246.    register GRECT *grect;        /* the input points */
  1247.  
  1248. {
  1249.    static WORD pts[4];            /* the resulting array */
  1250.  
  1251.    pts[0] = grect->g_x;            /* upper left */
  1252.    pts[1] = grect->g_y;
  1253.  
  1254.    pts[2] = grect->g_x + grect->g_w - 1;/* lower right */
  1255.    pts[3] = grect->g_y + grect->g_h - 1;
  1256.  
  1257.    return (pts);            /* return a pointer */
  1258. }
  1259.  
  1260. /* open main window for this application */
  1261.  
  1262. void pop_up ()
  1263.  
  1264. {
  1265.    GRECT rc;                /* work area */
  1266.  
  1267.    await |= MU_KEYBD;            /* also attend keyboard events */
  1268.  
  1269.    if (window[1].open)            /* #1 already opened? */
  1270.    {
  1271.       wind_set(topwin = window[1].win,WF_TOP);/* top it */
  1272.       return;
  1273.    }
  1274.  
  1275.    strcpy(window[1].title," KA9Q Internet Protocol Package ");/* set title */
  1276.    window[1].info[0] = 0; /* no info */
  1277.  
  1278.    window[1].org.g_x = window[0].work.g_x + gr_hwbox;
  1279.    window[1].org.g_y = window[0].work.g_y + (gl_mnid + 2) * gr_hhchar;
  1280.    window[1].org.g_w = 20 * gr_hwchar;
  1281.    window[1].org.g_h = gr_hhchar;
  1282.  
  1283.    if (!window[1].work.g_w)     /* first time? */
  1284.    {
  1285.       rc.g_w = window[0].work.g_w - gr_hwbox;
  1286.       rc.g_h = 10 * gr_hhbox;
  1287.       rc.g_x = window[0].work.g_x +
  1288.           (window[0].work.g_w - rc.g_w) / 2;
  1289.       rc.g_y = window[0].work.g_y + 3 * gr_hhbox;
  1290.       wi_align(&rc);         /* align on char bound */
  1291.    }
  1292.    else                 /* use existing size */
  1293.    {
  1294.       wind_calc(0,WI_KIND,window[1].work.g_x,
  1295.               window[1].work.g_y,
  1296.               window[1].work.g_w,
  1297.               window[1].work.g_h,
  1298.              &rc.g_x,&rc.g_y,&rc.g_w,&rc.g_h);
  1299.    }
  1300.  
  1301.    wi_open(1,WI_KIND,&rc);
  1302. }
  1303. #endif    /* GEM */
  1304.  
  1305. /* wait for interrupt (?) and return */
  1306. /* in normal version: check for stack overflows, spend time in system */
  1307. /* in desk-accessory version: await next command or timer event */
  1308.  
  1309. void eihalt ()
  1310.  
  1311. {
  1312. #ifdef GEM
  1313.    WORD button;                /* current button state */
  1314.    WORD bclicked;            /* mouse button clicks */
  1315.    WORD mx,my;                /* mouse position */
  1316.    WORD keycode;            /* code of pressed key */
  1317.    WORD keystat;            /* Shift/Ctrl/Alt state */
  1318.    WORD msgbuff[8];            /* incoming message buffer */
  1319.    WORD d;                /* dummy word */
  1320.    long count;                /* timer millisecond count */
  1321.    GRECT rc;                /* scratch */
  1322.    register WORD event;            /* event that occurred */
  1323.    register WINDOW *curwin;        /* scratch window pointer */
  1324. #endif
  1325.  
  1326. #ifndef GEMACC
  1327.    if (watchhw)
  1328.       Giaccess(watchhw,0x8f);        /* put reset on the printerport */
  1329.  
  1330.    if (STACK[0] != STKMAGIC ||        /* check the magic numbers */
  1331.        STACK[16] != STKMAGIC ||
  1332.        STACK[32] != STKMAGIC)
  1333.    {
  1334.       printf("Stack overflow!\n");
  1335.       while (kbread() == -1)
  1336.      ;
  1337.       doexit(0);
  1338.    }
  1339.  
  1340.    watchcnt = watchnet;            /* reset the watchdog */
  1341.    if (watchhw)
  1342.       Giaccess(0,0x8f);            /* put 00 on the printerport */
  1343. #endif
  1344.  
  1345. #ifdef GEM
  1346.    for (;;)
  1347.    {
  1348.       if (keyqueue)            /* still some key pending? */
  1349.      return;            /* then process it first */
  1350.  
  1351.       if (!fastpoll)            /* determine polling rate */
  1352.      count = 1000;            /* slow rate 1 second */
  1353.       else
  1354.       {
  1355.      count = 100;            /* fast rate .1 second */
  1356.  
  1357.      if (!--fastpoll)        /* check if next still fast */
  1358.      {
  1359.         if (window[1].cursor == 2)    /* unredrawn info in #1? */
  1360.            rq_redraw(window[1].win,&window[1].work);/* draw desired window */
  1361.      }
  1362.       }
  1363.  
  1364.       event = evnt_multi(await,        /* events we await */
  1365.          2,1,1,            /* BUTTON (left only) */
  1366.          m1flag,m1.g_x,m1.g_y,    /* M1 */
  1367.             m1.g_w,m1.g_h,
  1368.          m2flag,m2.g_x,m2.g_y,    /* M2 */
  1369.             m2.g_w,m2.g_h,
  1370.          msgbuff,        /* MESAG result */
  1371.          (WORD) count,(WORD) (count >> 16),/* TIMER */
  1372.          &mx,&my,        /* BUTTON, M1, M2 result */
  1373.          &button,&keystat,    /* BUTTON, M1, M2 result */
  1374.          &keycode,        /* KEYBD result */
  1375.          &bclicked);        /* BUTTON result */
  1376.  
  1377.       wind_get(DESK,WF_TOP,&topwin,&d,&d,&d);/* get current top window */
  1378.  
  1379.       if (event & MU_KEYBD)        /* keyboard input */
  1380.       {
  1381.      keyqueue = keycode;        /* save it */
  1382.      keyqstat = keystat;
  1383.      fastpoll = 50;            /* increase activity */
  1384.       }
  1385.  
  1386.       if (event & MU_MESAG)        /* incoming message? */
  1387.       {
  1388.      switch (msgbuff[0])        /* check message type */
  1389.      {
  1390. # ifdef GEMPRG
  1391.      case MN_SELECTED:        /* selected a menu item */
  1392.         mn_select(msgbuff[4]);    /* process selection */
  1393.         menu_tnormal(rs_trindex[MENUBAR],msgbuff[3],TRUE);/* title back to normal */
  1394.         break;
  1395. # endif
  1396.  
  1397.      case WM_REDRAW:        /* window redraw request */
  1398.         do_redraw(msgbuff[3],(GRECT *) &msgbuff[4]);
  1399.         break;
  1400.  
  1401.      case WM_TOPPED:        /* bring it to the top */
  1402.         curwin = &window[win[msgbuff[3]]]; /* point to window info */
  1403.         if (curwin->cursor == 2 &&     /* unredrawn info in it? */
  1404.         curwin->vert.pos >= curwin->vert.total - curwin->vert.seen)
  1405.            rq_redraw(curwin->win,&curwin->work);/* draw desired window */
  1406.  
  1407.         wind_set(topwin = msgbuff[3],WF_TOP);
  1408.         break;
  1409.  
  1410.      case WM_CLOSED:        /* window closed */
  1411.         wi_close(msgbuff[3]);    /* just close the window */
  1412.         break;
  1413.  
  1414.      case WM_FULLED:        /* wants to set to full screen */
  1415.         curwin = &window[win[msgbuff[3]]]; /* point to window info */
  1416.  
  1417.         wind_get(msgbuff[3],WF_CURRXYWH,&rc.g_x,&rc.g_y,&rc.g_w,&rc.g_h);
  1418.  
  1419.         if (rc_equal(&rc,&curwin->full))/* already fulled? */
  1420.         {
  1421.         wind_get(msgbuff[3],WF_PREVXYWH,&rc.g_x,&rc.g_y,&rc.g_w,&rc.g_h);
  1422.  
  1423.         graf_shrinkbox(rc.g_x,rc.g_y,rc.g_w,rc.g_h,
  1424.                    curwin->full.g_x,curwin->full.g_y,
  1425.                    curwin->full.g_w,curwin->full.g_h);
  1426.  
  1427.         wind_set(msgbuff[3],WF_CURRXYWH,rc.g_x,rc.g_y,rc.g_w,rc.g_h);
  1428.         }
  1429.         else
  1430.         {
  1431.         graf_growbox(rc.g_x,rc.g_y,rc.g_w,rc.g_h,
  1432.                  curwin->full.g_x,curwin->full.g_y,
  1433.                  curwin->full.g_w,curwin->full.g_h);
  1434.  
  1435.         wind_set(msgbuff[3],WF_CURRXYWH,curwin->full.g_x,curwin->full.g_y,
  1436.                         curwin->full.g_w,curwin->full.g_h);
  1437.         }
  1438.  
  1439.         wi_getwork(msgbuff[3]);    /* get new size */
  1440.         sl_update(msgbuff[3]);    /* update sliders */
  1441.         break;
  1442.  
  1443.      case WM_ARROWED:        /* clicked arrows/scrollbar */
  1444.         curwin = &window[win[msgbuff[3]]];/* point to window info */
  1445.  
  1446.         switch (msgbuff[4])        /* get more detail */
  1447.         {
  1448.         case 0:            /* page up */
  1449.         curwin->vert.pos -= curwin->vert.seen;
  1450.         break;
  1451.  
  1452.         case 1:            /* page down */
  1453.         curwin->vert.pos += curwin->vert.seen;
  1454.         break;
  1455.  
  1456.         case 2:            /* row up */
  1457.         curwin->vert.pos--;
  1458.         break;
  1459.  
  1460.         case 3:            /* row down */
  1461.         curwin->vert.pos++;
  1462.         break;
  1463.  
  1464.         case 4:            /* page left */
  1465.         curwin->hori.pos -= curwin->hori.seen;
  1466.         break;
  1467.  
  1468.         case 5:            /* page right */
  1469.         curwin->hori.pos += curwin->hori.seen;
  1470.         break;
  1471.  
  1472.         case 6:            /* column left */
  1473.         curwin->hori.pos--;
  1474.         break;
  1475.  
  1476.         case 7:            /* column right */
  1477.         curwin->hori.pos++;
  1478.         break;
  1479.         }
  1480.  
  1481.         sl_update(msgbuff[3]);    /* update sliders */
  1482.         rq_redraw(msgbuff[3],&curwin->work);/* draw desired window */
  1483.         break;
  1484.  
  1485.      case WM_HSLID:            /* moved horizontal slider */
  1486.         curwin = &window[win[msgbuff[3]]];/* point to window info */
  1487.         curwin->hori.pos = (long) msgbuff[4] *
  1488.                   (curwin->hori.total - curwin->hori.seen) / 1000L;
  1489.         sl_update(msgbuff[3]);    /* update sliders */
  1490.         rq_redraw(msgbuff[3],&curwin->work);/* draw desired window */
  1491.         break;
  1492.  
  1493.      case WM_VSLID:            /* moved vertical slider */
  1494.         curwin = &window[win[msgbuff[3]]];/* point to window info */
  1495.         curwin->vert.pos = (long) msgbuff[4] *
  1496.                   (curwin->vert.total - curwin->vert.seen) / 1000L;
  1497.         sl_update(msgbuff[3]);    /* update sliders */
  1498.         rq_redraw(msgbuff[3],&curwin->work);/* draw desired window */
  1499.         break;
  1500.  
  1501.      case WM_SIZED:            /* changed window size */
  1502.         if (msgbuff[6] < MIN_WIDTH) /* too small? */
  1503.         msgbuff[6] = MIN_WIDTH; /* set to minimal value */
  1504.  
  1505.         if (msgbuff[7] < MIN_HEIGHT)
  1506.         msgbuff[7] = MIN_HEIGHT;
  1507.  
  1508.         wi_align(&msgbuff[4]);    /* align on char bound */
  1509.         wind_set(msgbuff[3],WF_CURRXYWH,msgbuff[4],msgbuff[5],
  1510.                         msgbuff[6],msgbuff[7]);
  1511.  
  1512.         wi_getwork(msgbuff[3]);    /* update pos/size */
  1513.         sl_update(msgbuff[3]);    /* update sliders */
  1514.         break;
  1515.  
  1516.      case WM_MOVED:            /* moved to another pos */
  1517.         wi_align(&msgbuff[4]);    /* align on char bound */
  1518.         wind_set(msgbuff[3],WF_CURRXYWH,msgbuff[4],msgbuff[5],
  1519.                         msgbuff[6],msgbuff[7]);
  1520.  
  1521.         wi_getwork(msgbuff[3]);    /* update pos/size */
  1522.         break;
  1523.  
  1524.      case WM_NEWTOP:        /* placed on top */
  1525.         curwin = &window[win[msgbuff[3]]]; /* point to window info */
  1526.         if (curwin->cursor == 2 &&     /* unredrawn info in it? */
  1527.         curwin->vert.pos >= curwin->vert.total - curwin->vert.seen)
  1528.            rq_redraw(curwin->win,&curwin->work);/* draw desired window */
  1529.  
  1530.         topwin = msgbuff[3];    /* keep top window handle */
  1531.         break;
  1532.  
  1533. # ifdef GEMACC
  1534.      case AC_OPEN:            /* selected accessory */
  1535.         if (msgbuff[4] == gl_mnid)    /* doc says it's in word 3! */
  1536.            pop_up();        /* pop-up our main window */
  1537.         break;
  1538.  
  1539.      case AC_CLOSE:            /* removed accessory */
  1540.         if (msgbuff[3] == gl_mnid)    /* make sure it's for me! */
  1541.         {
  1542.            for (d = 1; d < USEWIN; d++)/* yep, so all user windows */
  1543.           window[d].open = FALSE;/* have been closed and deleted */
  1544.  
  1545.            for (d = 1; d < MAXWIN; d++)/* also get rid of handle info */
  1546.           win[d] = 0;
  1547.  
  1548.            topwin = -1;        /* WE are not on top anymore! */
  1549.            await &= ~MU_KEYBD;    /* keyboard not of interest */
  1550.         }
  1551.         break;
  1552. # endif
  1553.      }
  1554.  
  1555.      fastpoll = 50;            /* message came in, be active */
  1556.       }
  1557.  
  1558.       if (event & MU_TIMER)        /* time expired */
  1559.      return;            /* continue in KA9Q code */
  1560.    }
  1561. #else                    /* not GEM */
  1562.    /* spend some time in system, mainly for profiling */
  1563.    Vsync();
  1564.    Vsync();
  1565. #endif
  1566. }
  1567.  
  1568. #ifdef GEMACC
  1569. /* desk accessories must never exit... */
  1570.  
  1571. exit (val)
  1572.    int val;
  1573.  
  1574. {
  1575.    printf("\nAttempt to exit(%d)\n",val);
  1576.  
  1577.    for (;;)
  1578.    {
  1579.       keyqueue = 0;            /* force action */
  1580.       eihalt();
  1581.    }
  1582. }
  1583.  
  1584. # ifdef MWC
  1585. /* the allocation routines are not designed for desktop accessory use... */
  1586. /* define a replacement handler for low-level allocation */
  1587.  
  1588. extern int end[];            /* end of program */
  1589. static long Memtop = end;        /* current end of alloced space */
  1590. static char Mallocspace[50000L];    /* fixed area for dynamic allocation */
  1591. static long Mallocused = 0;        /* amount of area actually used now */
  1592.  
  1593. long lsbrk (incr)
  1594.     register long incr;        /* storage increment */
  1595.  
  1596. {
  1597.     register long rv;        /* return value */
  1598.  
  1599.     if (incr < 0)            /* decreasing top? */
  1600.         return (-1L);        /* not supported... */
  1601.  
  1602.     if (incr == 0)            /* query? */
  1603.         return (Memtop);    /* return current top */
  1604.  
  1605.     incr = (incr + 1) & 0xfffffffeL; /* incr must be even */
  1606.     rv = (long) (Mallocspace + Mallocused);/* compute address of new space */
  1607.  
  1608.     if ((rv + incr) > (long) (Mallocspace + sizeof(Mallocspace)))
  1609.         return (-1L);        /* return -1 on failure */
  1610.  
  1611.     Mallocused += incr;        /* reserve the space */
  1612.     Memtop = rv + incr;        /* set new top */
  1613.  
  1614.     return (rv);            /* and return the pointer */
  1615. }
  1616. # endif /* MWC */
  1617. #endif    /* GEM */
  1618.  
  1619. /* return the amount of free memory (= available from TOS) */
  1620.  
  1621. static long freemem ()
  1622.  
  1623. {
  1624. #ifdef GEMACC
  1625.    return ((long) sizeof(Mallocspace) - Mallocused);
  1626. #else
  1627.    long size;
  1628.    char *addr;
  1629.  
  1630.    if ((size = (long) Malloc(-1L)) < 256L) /* get largest available block */
  1631.       return (size);            /* when <= 256, stop looking */
  1632.  
  1633.    addr = (char *) Malloc(size);    /* acquire the specified block */
  1634.    size += freemem();            /* add this + any other blocks */
  1635.    Mfree(addr);                /* return the allocated block */
  1636.    return (size);            /* return total size */
  1637. #endif
  1638. }
  1639.  
  1640.  
  1641. /* Print free list map, when arg given also show free block sizes */
  1642. /* when numeric arg given, alloc/free that number of bytes */
  1643.  
  1644. int
  1645. memstat (argc,argv)
  1646.     int argc;
  1647.     char *argv[];
  1648.  
  1649. {
  1650. #ifdef MWC
  1651. /* This is severely dependent on the inner workings of the Mark Williams */
  1652. /* allocation routines...... */
  1653.  
  1654.     struct memblock {
  1655.         long size;
  1656.         struct memblock *next;
  1657.     };
  1658.     extern struct memblock *_a_scanp;
  1659.     register struct memblock *mp,*nmp;
  1660.     register long bsize;
  1661.     register int  bmode;
  1662.     int nblocks[2];
  1663.     long nbytes[2];
  1664.     long mxsize[2];
  1665.     int nchunks;
  1666.     char *lmalloc();
  1667.  
  1668.     if (argc > 1 && isdigit(argv[1][0])) {
  1669.         if ((mp = (struct memblock *) lmalloc(atol(argv[1]))) == 0) {
  1670.         printf("malloc failure\n");
  1671.         return 1;
  1672.         }
  1673.  
  1674.         free((char *) mp);
  1675.         return 0;
  1676.     }
  1677.  
  1678.     nblocks[0] = nblocks[1] = nchunks = 0;
  1679.     nbytes[0] = nbytes[1] = mxsize[0] = mxsize[1] = 0;
  1680.     if (argc > 1)
  1681.         printf("free block sizes:\n");
  1682.  
  1683.     mp = _a_scanp;                /* get start of blocklist */
  1684.     do {
  1685.         if (mp->size == 0){            /* zero size is a chain item */
  1686.         nchunks++;            /* count them */
  1687.         mp = mp->next;
  1688.         } else {
  1689.         if ((bsize = mp->size & ~1L) < 0)/* blocksize, always even */
  1690.             break;            /* quit when <0 (panic) */
  1691.  
  1692.         if (bmode = mp->size & 1)    /* 0=alloced, 1=free */
  1693.             /* when free, check if next also free */
  1694.             while ((nmp = (struct memblock *) ((char *) mp + bsize)) != _a_scanp &&
  1695.                (nmp->size & 1))
  1696.             /* join next (free) block with this one */
  1697.             mp->size = (bsize += nmp->size & ~1L) | 1;
  1698.  
  1699.         nblocks[bmode]++;
  1700.         nbytes[bmode] += bsize;
  1701.         if (bsize > mxsize[bmode])
  1702.             mxsize[bmode] = bsize;
  1703.         if (bmode && argc > 1)
  1704.             printf("%8ld",bsize - sizeof(long));
  1705.  
  1706.         ((char *) mp) += bsize;        /* advance ptr to next */
  1707.         }
  1708.     } while (mp != _a_scanp && nchunks < 1000); /* continue until complete round */
  1709.  
  1710.     if (argc > 1)
  1711.         printf("\n");
  1712.  
  1713.     if (bsize < 0)
  1714.         printf("panic: size of at least one block <0!! (%ld @ %06x)\n",
  1715.            mp->size,&mp->next);
  1716.  
  1717.     printf("%7ld bytes claimed in %d chunks\n",nbytes[0] + nbytes[1],nchunks);
  1718.     printf("%7ld bytes available from system\n",freemem());
  1719.     for (bmode = 0; bmode < 2; bmode++)
  1720.         printf("%7ld bytes %s in %3d blocks (largest %ld)\n",
  1721.             nbytes[bmode],(bmode? "free" : "used"),
  1722.             nblocks[bmode],mxsize[bmode] - sizeof(long));
  1723. #endif
  1724.     return 0;
  1725. }
  1726.  
  1727. #ifndef GEMACC
  1728. /* called at each timer tick.  decement a counter and RESET if it */
  1729. /* reaches zero.  the processor is already in supervisor mode. */
  1730.  
  1731. static void watchtick (timer_ms)
  1732. unsigned int timer_ms;
  1733.  
  1734. {
  1735.     if (watchcnt != 0 && --watchcnt == 0){
  1736.         *((long *) 0x3fc) = 0x53574154L; /* set signature (see log proc) */
  1737.         (**((int (**)()) 4L))();    /* jump via RESET vector */
  1738.     }
  1739.  
  1740.     (*orgtick)(timer_ms);        /* call original handler */
  1741. }
  1742. #endif
  1743.  
  1744. /* reboot the system.  this is used when the SYS_RESET function code */
  1745. /* is used on the "remote" server */
  1746.  
  1747. sysreset()
  1748.  
  1749. {
  1750.     Super(NULL);            /* switch to supervisor mode */
  1751.     *((long *) 0x3fc) = 0x0L;    /* remove signature (see log proc) */
  1752.     (**((int (**)()) 4L))();    /* jump via RESET vector */
  1753. }
  1754.  
  1755. #ifdef MWC
  1756. /* Select screen output mode */
  1757. int
  1758. doscreen (argc,argv)
  1759. int argc;
  1760. char *argv[];
  1761.  
  1762. {
  1763.     int conputc();            /* console putc routine */
  1764.  
  1765.     fflush(stdout);            /* first flush any pending output */
  1766.  
  1767.     switch (argv[1][0])            /* select on specified mode */
  1768.     {
  1769. # ifndef GEM
  1770.     case 'b':                /* using BIOS output */
  1771.     screenmode = SCRBIOS;
  1772.     stdout->_pt = conputc;
  1773.     break;
  1774. # endif
  1775.  
  1776.     case 'd':                /* using direct out to video */
  1777.     screenmode = SCRDIR;
  1778. # ifndef GEM
  1779.     linea0();            /* get line A info */
  1780.     logbase = Logbase();        /* read logical screen base */
  1781.     stdout->_pt = conputc;
  1782. # endif
  1783.     break;
  1784.  
  1785.     case 't':                /* using output via TOS */
  1786.     screenmode = SCRTOS;
  1787. # ifndef GEM
  1788.     stdout->_pt = _fputt;
  1789. # endif
  1790.     break;
  1791.  
  1792.     default:                /* unrecognized */
  1793.     return -1;            /* cmdparse will print options */
  1794.     }
  1795.  
  1796.     return 0;
  1797. }
  1798.  
  1799. /* utility routines for output redirection */
  1800.  
  1801. # ifndef GEM
  1802. /* put byte on AUX and/or console */
  1803. int conputc(c,outfile)
  1804.     int c;
  1805.     FILE *outfile;
  1806.  
  1807. {
  1808.     register char *set,*screen;
  1809.  
  1810.     if (outfile == stdout)
  1811.     {
  1812.     if (auxcon & AUXCOUT)        /* output to AUX? */
  1813.     {
  1814.         if (c == '\n')
  1815.         Bconout(1,auxotrn['\r']);
  1816.  
  1817.         Bconout(1,auxotrn[uchar(c)]);
  1818.     }
  1819.  
  1820.     if (auxcon & AUXCNOT)        /* no output to screen? */
  1821.         return c;
  1822.  
  1823.     if (cursor)            /* cursor still on? */
  1824.     {
  1825.         cursor = 0;
  1826.         Bconout(2,'\033');        /* switch it off */
  1827.         Bconout(2,'f');
  1828.     }
  1829.  
  1830.     switch (screenmode)
  1831.     {
  1832.     case SCRBIOS:            /* output via BIOS */
  1833.         if (c == '\n')
  1834.         Bconout(2,'\r');
  1835.  
  1836.         Bconout(2,c);
  1837.         return c;
  1838.  
  1839.     case SCRDIR:
  1840.         switch (c)            /* interpret char */
  1841.         {
  1842.         case '\n':            /* new line */
  1843.         Bconout(2,'\r');    /* also does a CR */
  1844.         if (V_CUR_CY == V_CEL_MY) /* check for last screen line */
  1845.         {
  1846.             while (Kbshift(-1) & 0x08) /* Alternate key pressed? */
  1847.             Vsync();    /* then hold screen */
  1848.  
  1849.             do_scroll(logbase); /* move it up by one line */
  1850.             V_CUR_CY--;        /* and tell that to line A */
  1851.         }
  1852.  
  1853.         case '\a':            /* bell */
  1854.         case '\b':            /* backspace */
  1855.         case '\t':            /* tab */
  1856.         case '\r':            /* carriage return */
  1857.         Bconout(2,c);
  1858.         break;
  1859.  
  1860.         case '\0':            /* NUL */
  1861.         break;
  1862.  
  1863.         default:
  1864.         set = (char *) V_FNT_AD + uchar(c);
  1865.         screen = (char *) V_CUR_AD; /* cursor address */
  1866.  
  1867.         switch (VPLANES)    /* depending on video mode... */
  1868.         {
  1869.         case 1:
  1870.             screen[ 0 * 80] = set[ 0 * 256]; /* blit the single character */
  1871.             screen[ 1 * 80] = set[ 1 * 256];
  1872.             screen[ 2 * 80] = set[ 2 * 256];
  1873.             screen[ 3 * 80] = set[ 3 * 256];
  1874.             screen[ 4 * 80] = set[ 4 * 256];
  1875.             screen[ 5 * 80] = set[ 5 * 256];
  1876.             screen[ 6 * 80] = set[ 6 * 256];
  1877.             screen[ 7 * 80] = set[ 7 * 256];
  1878.             screen[ 8 * 80] = set[ 8 * 256];
  1879.             screen[ 9 * 80] = set[ 9 * 256];
  1880.             screen[10 * 80] = set[10 * 256];
  1881.             screen[11 * 80] = set[11 * 256];
  1882.             screen[12 * 80] = set[12 * 256];
  1883.             screen[13 * 80] = set[13 * 256];
  1884.             screen[14 * 80] = set[14 * 256];
  1885.             screen[15 * 80] = set[15 * 256];
  1886.  
  1887.             if (V_CUR_CX < V_CEL_MX)
  1888.             {
  1889.             V_CUR_AD++;    /* simply increment addr */
  1890.             V_CUR_CX++;    /* and cursor position */
  1891.             }
  1892.             else
  1893.             {
  1894.             Bconout(2,'\r'); /* wrap cursor */
  1895.             if (V_CUR_CY == V_CEL_MY)
  1896.             {
  1897.                 do_scroll(logbase);
  1898.                 V_CUR_CY--;
  1899.             }
  1900.             Bconout(2,'\n');
  1901.             }
  1902.             break;
  1903.  
  1904.         case 2:
  1905.             screen[0 * 160] = screen[0 * 160 + 2] = set[0 * 256];
  1906.             screen[1 * 160] = screen[1 * 160 + 2] = set[1 * 256];
  1907.             screen[2 * 160] = screen[2 * 160 + 2] = set[2 * 256];
  1908.             screen[3 * 160] = screen[3 * 160 + 2] = set[3 * 256];
  1909.             screen[4 * 160] = screen[4 * 160 + 2] = set[4 * 256];
  1910.             screen[5 * 160] = screen[5 * 160 + 2] = set[5 * 256];
  1911.             screen[6 * 160] = screen[6 * 160 + 2] = set[6 * 256];
  1912.             screen[7 * 160] = screen[7 * 160 + 2] = set[7 * 256];
  1913.  
  1914.             if (V_CUR_CX < V_CEL_MX)
  1915.             {
  1916.             V_CUR_AD += (V_CUR_CX & 1)? 3 : 1;
  1917.             V_CUR_CX++;
  1918.             }
  1919.             else
  1920.             {
  1921.             Bconout(2,'\r'); /* wrap cursor */
  1922.             if (V_CUR_CY == V_CEL_MY)
  1923.             {
  1924.                 do_scroll(logbase);
  1925.                 V_CUR_CY--;
  1926.             }
  1927.             Bconout(2,'\n');
  1928.             }
  1929.             break;
  1930.         }
  1931.         break;
  1932.         }
  1933.         return c;
  1934.     }
  1935.     }
  1936.  
  1937.     return (_fputt(c,outfile));        /* call original out char routine */
  1938. }
  1939. # endif
  1940.  
  1941. /* route standard I/O to the AUX port, to facilitate control via a */
  1942. /* terminal connected to this port */
  1943. /* Usage: auxcon <io code> [<baudrate> [7|8 [noflow]]] */
  1944. /* <io code> is 10 (input) | 01 (output) | 02 (no screen output) */
  1945. int
  1946. doauxcon(argc,argv)
  1947. int argc;
  1948. char *argv[];
  1949. {
  1950.    register int i;
  1951.    int sp = -1;                /* default = unchanged speed */
  1952.    int ucr = 0x88;            /* 8bit, no parity, 1 stopbit */
  1953.    int flow = 1;            /* default = XON/XOFF flow ctrl */
  1954.  
  1955. # ifndef GEM
  1956.    stdout->_pt = (screenmode == SCRTOS)? _fputt : conputc;
  1957. # endif
  1958.  
  1959.    if ((auxcon = htoi(argv[1])) == 0)    /* convert arg, 0 = off */
  1960.       return 0;
  1961.  
  1962.    for (i = 0; i < nasy; i++)
  1963.       if (asy[i].addr == RS232)
  1964.       {
  1965.      auxcon = 0;
  1966.      printf("AUX: already in use for SLIP or KISS\n");
  1967.      return 1;
  1968.       }
  1969.  
  1970.    for (i = 0; i < 256; i++)        /* init translate tables */
  1971.       auxotrn[i] = auxitrn[i] = i;
  1972.  
  1973.    auxotrn[0x11] = auxotrn[0x13] = '.'; /* inhibit XON and XOFF */
  1974.  
  1975.    /* set some keycodes. these values are especially good for the */
  1976.    /* EPSON PX-8. this should be made variable for other terminals. */
  1977.  
  1978.    auxitrn[0x03] = -2;            /* STOP = command mode (F10) */
  1979.    auxitrn[0x0b] = 0x12;        /* HOME = retype line (^R) */
  1980.    auxitrn[0x12] = -12;            /* INS    = insert mode */
  1981.    auxitrn[0x1c] = -16;            /* RIGHT */
  1982.    auxitrn[0x1d] = -17;            /* LEFT */
  1983.    auxitrn[0x1e] = -19;            /* UP */
  1984.    auxitrn[0x1f] = -14;            /* DOWN */
  1985.  
  1986.    if (argc > 2)
  1987.       switch (atoi(argv[2]))
  1988.       {
  1989.       case 19200:  sp = 0; break;
  1990.       case  9600:  sp = 1; break;
  1991.       case  4800:  sp = 2; break;
  1992.       case  3600:  sp = 3; break;
  1993.       case  2400:  sp = 4; break;
  1994.       case  1200:  sp = 7; break;
  1995.       case   600:  sp = 8; break;
  1996.       case   300:  sp = 9; break;
  1997.       default:
  1998.      printf("auxcon: unsupported speed %s (speed unchanged)\n",argv[2]);
  1999.      break;
  2000.       }
  2001.  
  2002.    if (argc > 3 && argv[3][0] == '7')
  2003.    {
  2004.       ucr = 0x0ae;        /* 7 bit, even parity, 1 stopbit */
  2005.  
  2006.       for (i = 128; i < 256; i++)
  2007.       {
  2008.      auxotrn[i] = '.';
  2009.      auxitrn[i] = auxitrn[i - 128];
  2010.       }
  2011.    }
  2012.  
  2013.    if (argc > 4 && argv[4][0] == 'n')
  2014.       flow = 0;            /* no flow control */
  2015.  
  2016.    Rsconf(sp,flow,ucr,0x01,0x01,0x00); /* set the MFP serial channel */
  2017. # ifndef GEM
  2018.    if (auxcon & AUXCOUT)    /* output to AUX selected? */
  2019.       stdout->_pt = conputc;    /* set replacement "put char" routine */
  2020. # endif
  2021.    return 0;
  2022. }
  2023. #endif
  2024.  
  2025.  
  2026. /* Print or Set the date and time */
  2027.  
  2028. dodate(argc,argv)
  2029.     int argc;
  2030.     char *argv[];
  2031. {
  2032.     long tloc;
  2033.     register char *p;
  2034.     int y,m,d,h,n,s;
  2035.     extern long starttime;
  2036. #ifdef MWC
  2037.     extern long _boottime;
  2038. #endif
  2039.  
  2040.     if (argc < 2) {
  2041.         time(&tloc);
  2042.         printf("%s",ctime(&tloc));
  2043.         return 0;
  2044.     }
  2045.  
  2046.  
  2047.     if (*(p = argv[1]) == 's') {    /* starttime */
  2048.         printf("starttime: %s",ctime(&starttime));
  2049.         return 0;
  2050.     }
  2051.  
  2052. #ifdef MWC
  2053.     if (*p == 'b') {        /* boottime */
  2054.         time(&tloc);
  2055.         printf("boottime: %s",ctime(&_boottime));
  2056.         return 0;
  2057.     }
  2058. #endif
  2059.  
  2060.     if (strlen(p) != 10 && strlen(p) != 12)
  2061.         return -1;
  2062.     while (*p)
  2063.         if (!isdigit(*p++))
  2064.         return -1;
  2065.  
  2066.     p = argv[1];
  2067.     m = 10 * p[0] + p[1] - 11 * '0';
  2068.     d = 10 * p[2] + p[3] - 11 * '0';
  2069.     h = 10 * p[4] + p[5] - 11 * '0';
  2070.     n = 10 * p[6] + p[7] - 11 * '0';
  2071.     s = 10 * p[8] + p[9] - 11 * '0';
  2072.     if (p[10]) {
  2073.         if ((y = 10 * p[10] + p[11] - 11 * '0' + 1900) < 1980)
  2074.         y += 100;
  2075.     } else
  2076.         y = ((Tgetdate() >> 9) & 0x7f) + 1980;
  2077.  
  2078.     if (Tsettime((h << 11) | (n << 5) | (s >> 1)) ||
  2079.         Tsetdate(((y - 1980) << 9) | (m << 5) | d))
  2080.         return -1;
  2081.  
  2082.     Settime(((long) (y - 1980) << 25) | ((long) m << 21) | ((long) d << 16) |
  2083.         ((long) h << 11) | (n << 5) | (s >> 1));
  2084.  
  2085. #ifdef MWC
  2086.     _boottime = 0;            /* reset MW's idea of time */
  2087. #endif
  2088.     return 0;
  2089. }
  2090.  
  2091. /* Spawn subshell */
  2092. /* extension: "shell =name" sets the program to run as a shell */
  2093.  
  2094. doshell(argc,argv)
  2095.     int argc;
  2096.     char *argv[];
  2097. {
  2098.     static char command[80] = "COMMAND.PRG";
  2099. #ifndef GEM
  2100.     char tail[128], *p = tail + 1,**envp;
  2101.     int  arg,len;
  2102.     long ret;
  2103.     int  curdrive;
  2104.     char curdir[66];
  2105. #endif
  2106.  
  2107.     if (argc == 2 && argv[1][0] == '=')
  2108.     {
  2109.         strcpy(command,argv[1] + 1);
  2110.         return 0;
  2111.     }
  2112.  
  2113. #ifdef GEM
  2114.     printf("No shell in GEM version...\n");
  2115. #else
  2116.     curdrive = Dgetdrv();        /* get current drive (0,1,...) */
  2117.  
  2118.     if (Dgetpath(tail,0) == 0){    /* get current dir */
  2119.         sprintf(curdir,"%c:%s%s",(char) curdrive+'A',
  2120.             (*tail? "" : "\\"),tail);
  2121.     }
  2122.  
  2123.     tail[0] = 0;
  2124.  
  2125.     for (arg = 1; arg < argc; arg++)
  2126.     {
  2127.         len = strlen(argv[arg]);
  2128.  
  2129.         if ((len + tail[0]) < 127)
  2130.         {
  2131.             if (tail[0])
  2132.             {
  2133.                 *p++ = ' ';
  2134.                 tail[0]++;
  2135.             }
  2136.  
  2137.             strcpy(p,argv[arg]);
  2138.             p += len;
  2139.             tail[0] += len;
  2140.         }
  2141.         else
  2142.             break;
  2143.     }
  2144.  
  2145. # ifdef SERVERS
  2146.     if (logfp != NULLFILE)        /* logging to file? */
  2147.         fclose(logfp);
  2148. # endif
  2149.  
  2150. # ifdef TRACE
  2151.     if (trfp != stdout)        /* trace to file? */
  2152.         fclose(trfp);        /* close it during shell exec */
  2153. # endif
  2154.  
  2155.     Bconout(2,'\033');        /* make sure cursor is ON */
  2156.     Bconout(2,'e');
  2157.     cursor = 1;
  2158.  
  2159.     watchcnt = watchsh;        /* set SHELL watchdog time */
  2160.  
  2161.     if ((envp = make_env()) != NULLCHRP)
  2162.         ret = Pexec(0,command,tail,envp[0]);
  2163.     else
  2164.         ret = Pexec(0,command,tail,NULLCHRP);
  2165.  
  2166.     free_env();
  2167.  
  2168.     Dsetdrv(curdrive);        /* reset current directory */
  2169.     Dsetpath(curdir);
  2170.  
  2171. # ifdef SERVERS
  2172.     if (logfp != NULLFILE)        /* re-open logfile if it was open */
  2173.         logfp = fopen(logname,"a+");
  2174. # endif
  2175.  
  2176. # ifdef TRACE
  2177.     if (trfp != stdout)        /* re-open tracefile if not stdout */
  2178.         if ((trfp = fopen(trname,"a+")) == NULLFILE)
  2179.             trfp = stdout;
  2180. # endif
  2181.  
  2182.     Bconout(2,'\033');        /* make sure cursor is ON */
  2183.     Bconout(2,'e');
  2184.     Bconout(2,'\033');        /* autoWRAP ON */
  2185.     Bconout(2,'v');
  2186.  
  2187.     if (ret < 0)
  2188.     {
  2189.         printf("%s: cannot execute\n",command);
  2190.         return -1;
  2191.     }
  2192. #endif
  2193.  
  2194.     return 0;
  2195. }
  2196.  
  2197. /* control the watchdog function */
  2198.  
  2199. dowatchdog (argc,argv)
  2200.     int argc;
  2201.     char *argv[];
  2202. {
  2203. #ifndef GEMACC
  2204.     unsigned int timer_ms = Tickcal();
  2205.  
  2206.     switch (argc)
  2207.     {
  2208.     case 4:
  2209.         watchhw = atoi(argv[3]) & 0xff;
  2210.     case 3:
  2211.         watchsh = (atol(argv[2]) * 1000) / timer_ms;
  2212.     case 2:
  2213.         watchnet = (atol(argv[1]) * 1000) / timer_ms;
  2214.         break;
  2215.     case 1:
  2216.         printf("NET=%ld SHELL=%ld HW=%u\n",
  2217.             (watchnet * timer_ms) / 1000,
  2218.             (watchsh * timer_ms) / 1000,watchhw);
  2219.         break;
  2220.     }
  2221. #endif
  2222.     return 0;
  2223. }
  2224.  
  2225. /* interrupt latency measurement (for testing and optimizing purposes) */
  2226.  
  2227. #define MODE        4
  2228. #define PRESCALE    50
  2229. #define CLOCK        2457600L
  2230.  
  2231. unsigned long latency[256];
  2232. static int lat_active = 0;
  2233.  
  2234. static void lat_stop ()
  2235.  
  2236. {
  2237.     Xbtimer(0,0,0,NULLVFP);        /* stop timer & remove handler */
  2238. }
  2239.  
  2240. static void lat_start ()
  2241.  
  2242. {
  2243.     extern void lathandler();
  2244.  
  2245.     Xbtimer(0,MODE,255,lathandler);    /* init timer and start it */
  2246. }
  2247.  
  2248. /* start/stop/display latency measurement */
  2249.  
  2250. dolatency (argc,argv)
  2251.     int argc;
  2252.     char *argv[];
  2253. {
  2254.     switch (tolower(argv[1][0]))
  2255.     {
  2256.     case 'n':                /* stop measurement */
  2257.     lat_stop();
  2258.     lat_active = 0;
  2259.     break;
  2260.  
  2261.     case 'y':                /* clear & start measurement */
  2262.     lat_stop();            /* first stop it for safety */
  2263.     memset(latency,0,sizeof(latency)); /* clear the tallies */
  2264.     lat_start();            /* start measurement */
  2265.     lat_active = 1;
  2266.     break;
  2267.  
  2268.     case 'r':                /* show raw data */
  2269.     lat_raw();
  2270.     break;
  2271.  
  2272.     case 's':                /* show status (results) */
  2273.     if (!lat_active) {
  2274.         printf("latency measurement not active\n");
  2275.         break;
  2276.     }
  2277.     lat_stop();
  2278.     lat_status();
  2279.     lat_start();
  2280.     break;
  2281.  
  2282.     default:
  2283.     return -1;
  2284.     }
  2285.  
  2286.     return 0;
  2287. }
  2288.  
  2289. static char *pltime (timer)
  2290.     unsigned int timer;
  2291.  
  2292. {
  2293.     static char buf[80];
  2294.  
  2295.     timer = 255 - timer;        /* compute elapsed time */
  2296.  
  2297.     sprintf(buf,"%6lu us",(1000000L*PRESCALE*timer)/CLOCK);
  2298.  
  2299.     if (timer != 0)
  2300.     sprintf(buf + 9,", %6lu/s, %6lu bps",CLOCK/((long) PRESCALE*timer),
  2301.                      (CLOCK*8)/((long) PRESCALE*timer));
  2302.  
  2303.     return buf;
  2304. }
  2305.  
  2306. static void lat_raw ()
  2307.  
  2308. {
  2309.     unsigned int i = 255;
  2310.  
  2311.     do
  2312.     {
  2313.     if (latency[i] != 0)
  2314.         printf("%9lu * %s\n",latency[i],pltime(i));
  2315.     }
  2316.     while (i-- != 0);
  2317. }
  2318.  
  2319. static void lat_status ()
  2320.  
  2321. {
  2322.     unsigned int i,j;
  2323.     unsigned int lng,max;
  2324.     unsigned long maxv,totl,ttl,lim;
  2325.  
  2326.     for (lng = 0; lng < 256; lng++)    /* find longest latency */
  2327.     if (latency[lng] != 0)
  2328.         break;
  2329.  
  2330.     maxv = totl = 0;
  2331.  
  2332.     for (i = lng; i < 256; i++)
  2333.     {
  2334.     totl += latency[i];        /* count total number of ticks */
  2335.  
  2336.     if (latency[i] > maxv)        /* record largest number */
  2337.     {
  2338.         maxv = latency[i];        /* found a larger number! */
  2339.         max = i;
  2340.     }
  2341.     }
  2342.  
  2343.     printf("%lu samples\n\n",totl);    /* print total number of samples */
  2344.  
  2345.     printf("normal:  %s (%lu%%)\n",pltime(max),maxv / (totl / 100L));
  2346.     printf("longest: %s (%lu)\n\n",pltime(lng),latency[lng]);
  2347.  
  2348.     lim = 1;
  2349.  
  2350.     for (j = 1; j < 10; j++)        /* 10^1 .. 10^9 */
  2351.     {
  2352.     lim *= 10;
  2353.  
  2354.     if ((maxv = (totl / lim)) == 0) /* total #ticks / 10^n */
  2355.         break;
  2356.  
  2357.     ttl = 0;
  2358.  
  2359.     for (i = 0; i < 256; i++)
  2360.         if ((ttl += latency[i]) > maxv)
  2361.         break;
  2362.  
  2363.     printf("<1 in 10^%d longer than %s\n",j,pltime(i));
  2364.     }
  2365. }
  2366.  
  2367. #if (defined(MWC) && (MWC < 306))
  2368. /* Mark Williams C has a bug that influences '>> 16' constructs... */
  2369. /* when you shift a long value by exactly 16 bits, the compiler */
  2370. /* cleverly codes a "swap" and "ext.w", it should have been "ext.l" */
  2371. /* we define this variable to generate less-optimized code (see global.h) */
  2372. /* The bug was fixed in 3.0.6 (according to Howard Chu) */
  2373.  
  2374. int Sixteen = 16;            /* shift for hiword... */
  2375. #endif
  2376.  
  2377.